﻿using Net.Event;
using System;
using System.Threading;

namespace Net.System
{
    public abstract class WorkerItemBase
    {
        public abstract void Invoke();
    }

    public class WorkerItem1 : WorkerItemBase
    {
        public readonly Action callback;

        public WorkerItem1(Action callback)
        {
            this.callback = callback;
        }

        public override void Invoke()
        {
            callback();
        }
    }

    public class WorkerItem2 : WorkerItemBase
    {
        public readonly WaitCallback callback;
        public readonly object state;

        public WorkerItem2(WaitCallback callback, object state)
        {
            this.callback = callback;
            this.state = state;
        }

        public override void Invoke()
        {
            callback(state);
        }
    }

    public class ThreadWorker
    {
        private Thread thread;
        private readonly QueueSafe<WorkerItemBase> workerQueue = new();
        private readonly AutoResetEvent workerAvailable = new(false);
        private readonly ManualResetEvent stopEvent = new(false);

        public Action<Exception> OnException { get; set; }
        public bool Idle { get; internal set; }

        public void Start(string name)
        {
            thread = new Thread(DoWorker)
            {
                Name = name,
                IsBackground = true
            };
            thread.Start();
        }

        private void DoWorker()
        {
            while (!stopEvent.WaitOne(0))
            {
                Idle = true;
                workerAvailable.WaitOne();
                Idle = false;
                while (workerQueue.TryDequeue(out var workerItem))
                {
                    try
                    {
                        workerItem.Invoke();
                    }
                    catch (Exception ex)
                    {
                        if (OnException == null)
                            NDebug.LogError("线程池异常:" + ex);
                        else
                            OnException(ex);
                    }
                }
            }
        }

        public void QueueWorker(Action callback)
        {
            workerQueue.Enqueue(new WorkerItem1(callback));
            workerAvailable.Set();
        }

        public void QueueWorker(WaitCallback callback, object state)
        {
            workerQueue.Enqueue(new WorkerItem2(callback, state));
            workerAvailable.Set();
        }

        public void Stop()
        {
            stopEvent.Set();
            workerAvailable.Set();
            thread.Join();
        }

        public override string ToString()
        {
            return $"Name:{thread.Name} IsIdle:{Idle} Queue:{workerQueue.Count}";
        }
    }
}